#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

typedef void *pvoid_t;
typedef uint8_t *pbyte_t;

typedef struct tag_node {
  pvoid_t data;
  struct tag_node *prev, *next;
} node_t, *pnode_t;

typedef struct tag_list {
  uint32_t count;
  pnode_t front, back;
} list_t, *plist_t;

typedef struct tag_creature {
  pbyte_t name;
  uint16_t hit_points, experience;
} creature_t, *pcreature_t;

plist_t initialize() {
  return calloc(1, sizeof(list_t));
}

uint32_t size(const plist_t list) {
  return list && list->count ? list->count : 0;
}

uint8_t empty(const plist_t list) {
  return !list || !size(list);
}

pnode_t front(const plist_t list) {
  return list && list->front ? list->front : 0;
}

pnode_t back(const plist_t list) {
  return list && list->back ? list->back : 0;
}

pvoid_t erase(const plist_t list, const pnode_t node) {
  if(empty(list) || !node)
    return 0;

  if(node == list->front && node == list->back)
    list->front = list->back = 0;
  else
    if(node == list->front) {
      list->front = node->next;
      list->front->prev = 0;
    } else
      if (node == list->back) {
        list->back = node->prev;
        list->back->next = 0;
      } else {
        pnode_t
          before = node->prev,
          after = node->next;

        after->prev = before;
        before->next = after;
      }

  --list->count;
  pvoid_t data = node->data;
  free(node);

  return data;
}

void push_front(const plist_t list, const pvoid_t data) {
  if(!list)
    return;

  pnode_t node = calloc(1, sizeof(node_t));
  if(!node)
    return;

  if(!list->front)
    list->front = list->back = node;
  else {
    node->next = list->front;
    list->front->prev = node;
    list->front = node;
  }

  node->data = data;
  ++list->count;
}

void push_back(const plist_t list, const pvoid_t data) {
  if(!list)
    return;

  pnode_t node = calloc(1, sizeof(node_t));
  if(!node)
    return;

  if(!list->back)
    list->front = list->back = node;
  else {
    list->back->next = node;
    node->prev = list->back;
    list->back = node;
  }

  node->data = data;
  ++list->count;
}

pvoid_t pop_front(const plist_t list) {
  return erase(list, list->front);
}

pvoid_t pop_back(const plist_t list) {
  return erase(list, list->back);
}

void destroy(const plist_t list) {
  if(!list)
    return;

  pnode_t j = list->front;
  for(; j != 0; j = j->next)
    if(j->prev)
      free(j->prev);

  if(list->back)
    free(list->back);
  free(list);
}

void list_dump(const plist_t list) {
  printf(
    "list addr\t\t\t0x%x\n"
    "size\t\t\t\t%u\n"
    "\n"
    "front node addr\t\t\t0x%x\n"
    "front data addr\t\t\t0x%x\n"
    "\n"
    "back node addr\t\t\t0x%x\n"
    "back data addr\t\t\t0x%x\n"
    "\n",
    list,
    size(list),
    front(list),
    front(list)->data,
    back(list),
    back(list)->data
  );
}

void list_creature_dump(const plist_t list) {
  pcreature_t current_front = (pcreature_t)front(list)->data,
    current_back = (pcreature_t)back(list)->data;

  printf(
    "front creature name\t\t%s\n"
    "front creature hit points\t%u\n"
    "front creature experience\t%u\n"
    "\n"
    "back creature name\t\t%s\n"
    "back creature hit points\t%u\n"
    "back creature experience\t%u\n"
    "\n",
    current_front->name,
    current_front->hit_points,
    current_front->experience,
    current_back->name,
    current_back->hit_points,
    current_back->experience
  );
}

int32_t main(int32_t argc, uint8_t **argv) {
  plist_t list = initialize();
  if(!list)
    return -1;

  pcreature_t
    troll = calloc(1, sizeof(creature_t)),
    orc = calloc(1, sizeof(creature_t)),
    goblin = calloc(1, sizeof(creature_t));

  if(!troll || !orc || !goblin)
    return -2;

  troll->name = "Troll";
  orc->name = "Orc";
  goblin->name = "Goblin";

  troll->hit_points = 50;
  orc->hit_points = 70;
  goblin->hit_points = 75;

  troll->experience = 20;
  orc->experience = 25;
  goblin->experience = 52;

  push_back(list, orc);
  push_back(list, goblin);
  push_front(list, troll);

  pvoid_t last_front = pop_front(list);
  push_front(list, last_front);

  list_dump(list);
  list_creature_dump(list);

  free(troll);
  free(orc);
  free(goblin);

  destroy(list);
  return 0;
}
